#include <cstdint>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <vector>

#include <include/core/SkCanvas.h>
#include <include/core/SkFont.h>
#include <include/core/SkFontMetrics.h>
#include <include/core/SkTextBlob.h>
#include <include/core/SkTypeface.h>
#include <hb.h>
#include <skia_framework.h>
#include <unicode/brkiter.h>
#include <unicode/utf8.h>

using namespace icu;

namespace {
hb_blob_t *HbFaceReferenceTableSkTypeface(hb_face_t *face, hb_tag_t tag, void *context)
{
    SkTypeface* typeface = reinterpret_cast<SkTypeface*>(context);
    if (typeface == nullptr) {
        return nullptr;
    }

    const size_t tableSize = typeface->getTableSize(tag);
    if (tableSize == 0) {
        return nullptr;
    }

    void* buffer = malloc(tableSize);
    if (buffer == nullptr) {
        return nullptr;
    }

    size_t actualSize = typeface->getTableData(tag, 0, tableSize, buffer);
    if (tableSize != actualSize) {
        free(buffer);
        return nullptr;
    }

    return hb_blob_create(reinterpret_cast<char *>(buffer),
                          tableSize, HB_MEMORY_MODE_WRITABLE, buffer, free);
}
} // namespace

class Typography {
public:
    std::string  text_ = "";
    int32_t      size_ = 16;
    int32_t      maxlines_ = 1e9;
    std::string  ellipsis_ = "";
    bool         directionIsRight_ = false;
    double       letterSpacing_ = 0;
    double       wordSpacing_ = 0;
    bool         isEllipsis_ = false;
    bool         underline = false;

    void Layout(double lineWidth)
    {
        lineWidth_ = lineWidth;
        printf("layouting: '%s'\n", text_.c_str());
        printf("- size: %d, maxlines: %d\n", size_, maxlines_);
        printf("- ellipsis_: '%s', directionIsRight_: %s\n",
               ellipsis_.c_str(), directionIsRight_ ? "true" : "false");
        printf("- letterSpacing: %lf, wordSpacing: %lf\n", letterSpacing_, wordSpacing_);

        // 转化码点
        std::vector<uint16_t> u16text;
        {
            const auto &slen = text_.size();
            for (int i = 0; i < slen;) {
                uint32_t codepoint = 0;
                U8_NEXT(text_.data(), i, slen, codepoint);
                if (U16_LENGTH(codepoint) == 1) {
                    u16text.push_back(codepoint);
                } else {
                    u16text.push_back(U16_LEAD(codepoint));
                    u16text.push_back(U16_TRAIL(codepoint));
                }
            }
        }

        // 获得边界
        std::queue<int> wordBoundary;
        {
            UErrorCode errcode = U_ZERO_ERROR;
            const auto wb = BreakIterator::createWordInstance(Locale::getDefault(), errcode);
            wb->setText(u16text.data());
            for (auto i = wb->first(); i != wb->DONE; i = wb->next()) {
                wordBoundary.push(i);
            }
            // 去掉第一个wb->first()
            wordBoundary.pop();
            delete wb;
        }

        // 获得字体
        SkFont sfont;
        sk_sp<SkTypeface> stypeface = SkTypeface::MakeFromFile("out/NotoSansArabic-Regular.ttf");
        {
            sfont.setTypeface(stypeface);
            sfont.setSize(size_);
        }
        double em = stypeface->getUnitsPerEm();

        // 塑型
        const auto &hbuffer = hb_buffer_create();
        hb_face_t *hface = nullptr;
        hb_font_t *hfont = nullptr;
        {
            hb_buffer_add_utf16(hbuffer, u16text.data(), u16text.size(), 0, -1);
            hb_buffer_set_direction(hbuffer, HB_DIRECTION_LTR);
            hb_buffer_set_script(hbuffer, HB_SCRIPT_LATIN);
            hb_buffer_set_language(hbuffer, hb_language_from_string("en", -1));
            hface = hb_face_create_for_tables(HbFaceReferenceTableSkTypeface, stypeface.get(), 0);
            hfont = hb_font_create(hface);
            hb_shape(hfont, hbuffer, nullptr, 0);
        }
        uint32_t ng = 0u;
        auto hginfos = hb_buffer_get_glyph_infos(hbuffer, &ng);
        auto hgposes = hb_buffer_get_glyph_positions(hbuffer, nullptr);

        // 布局
        {
            bool isEllipsisValid = false;
            if (ellipsis_.length()) {
                // 存在省略号
                ellipsisTypography_ = std::make_unique<Typography>();
                ellipsisTypography_->isEllipsis_ = true;
                ellipsisTypography_->text_ = ellipsis_;
                ellipsisTypography_->Layout(1e9);
                printf("ellipsisWidth: %d\n", ellipsisTypography_->GetLastLineWidth());
            }

            glyphs_.clear();
            int32_t offsetX = 0;
            int32_t offsetY = 0;
            int32_t linenum = 1;
            double lwidth = lineWidth;
            const auto &lineHeight = GetLineHeight();
            if (ellipsisTypography_ && (linenum == maxlines_)) {
                // 存在省略号
                lwidth -= ellipsisTypography_->GetLastLineWidth();
            }

            for (auto i = 0u; i < ng; i++) {
                // 计算宽度
                double letterWidth = hgposes[i].x_advance + hgposes[i].x_offset;
                letterWidth *= size_ / em;

                // 处理越界
                if (offsetX + letterWidth > lwidth) {
                    // 文本靠右
                    if (directionIsRight_) {
                        auto wdelta = lwidth - GetLastLineWidth();
                        for (auto j = glyphs_.size() - 1; j >= 0; j--) {
                            if (glyphs_[j].posY != offsetY) {
                                break;
                            }

                            glyphs_[j].posX += wdelta;
                        }

                        // 特殊处理省略号
                        if (linenum > maxlines_ && ellipsisTypography_) {
                            ellipsisOffsetX_ += wdelta;
                        }
                    }

                    linenum++;
                    // 存在省略号
                    if (ellipsisTypography_ && (linenum == maxlines_)) {
                        lwidth -= ellipsisTypography_->GetLastLineWidth();
                    } else if (linenum > maxlines_) {
                        // 额外显示省略号
                        if (ellipsisTypography_) {
                            ellipsisOffsetX_ += offsetX;
                            ellipsisOffsetY_ += offsetY;
                            isEllipsisValid = true;
                        }

                        // 剩下的不显示
                        break;
                    }

                    offsetX = 0;
                    offsetY += lineHeight;
                }

                // 放下字元
                printf("  - %c [%.0lfx%.0lf] {%d, %d}\n", u16text[i], letterWidth, lineHeight, offsetX, offsetY);
                glyphs_.push_back({
                    .glyphid = hginfos[i].codepoint,
                    .posX = offsetX,
                    .posY = offsetY,
                    .width = letterWidth,
                });

                // 下一个字元的偏移量
                offsetX += letterWidth;
                offsetX += letterSpacing_;
                if (!wordBoundary.empty() && i == wordBoundary.front()) {
                    offsetX += wordSpacing_;
                    wordBoundary.pop();
                    // 不要一个长度的边界
                    if (!wordBoundary.empty() && i == wordBoundary.front() - 1) {
                        wordBoundary.pop();
                    }
                }
            }

            if (directionIsRight_) {
                auto wdelta = lwidth - GetLastLineWidth();
                for (auto j = glyphs_.size() - 1; j >= 0; j--) {
                    if (glyphs_[j].posY != offsetY) {
                        break;
                    }

                    glyphs_[j].posX += wdelta;
                }
            }

            if (!isEllipsisValid) {
                ellipsisTypography_ = nullptr;
            } else {
                printf(" - ellipsisOffset: %f, %f\n", ellipsisOffsetX_, ellipsisOffsetY_);
            }
        }

        // 垃圾回收
        {
            hb_buffer_destroy(hbuffer);
            hb_font_destroy(hfont);
            hb_face_destroy(hface);
        }
    }

    int GetLastLineWidth()
    {
        if (glyphs_.empty()) {
            return 0;
        }

        return glyphs_.back().posX + glyphs_.back().width;
    }

    double GetLineHeight()
    {
        SkFont sfont;
        sfont.setTypeface(SkTypeface::MakeFromFile("out/NotoSansArabic-Regular.ttf"));
        sfont.setSize(size_);
        SkFontMetrics smetrics;
        sfont.getMetrics(&smetrics);
        return smetrics.fDescent - smetrics.fAscent;
    }

    int GetUnderlinePos()
    {
        SkFont sfont;
        sfont.setTypeface(SkTypeface::MakeFromFile("out/NotoSansArabic-Regular.ttf"));
        sfont.setSize(size_);
        SkFontMetrics smetrics;
        sfont.getMetrics(&smetrics);
        return smetrics.fUnderlinePosition;
    }

    void Paint(SkCanvas &canvas, float offsetx, float offsety)
    {
        if (!isEllipsis_) {
            SkPaint paint;
            paint.setAntiAlias(true);
            paint.setARGB(255, 0, 127, 0);
            paint.setStyle(paint.kStroke_Style);
            canvas.drawRect({offsetx, offsety, offsetx + lineWidth_, offsety + 190}, paint);
        }

        SkFont sfont;
        sfont.setTypeface(SkTypeface::MakeFromFile("out/NotoSansArabic-Regular.ttf"));
        sfont.setSize(size_);

        SkTextBlobBuilder builder;
        const auto &blob = builder.allocRunPos(sfont, glyphs_.size());
        for (int i = 0; i < glyphs_.size(); i++) {
            blob.glyphs[i] = glyphs_[i].glyphid;
            blob.pos[i * 2 + 0] = glyphs_[i].posX;
            blob.pos[i * 2 + 1] = glyphs_[i].posY;
        }

        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setARGB(255, 255, 0, 0);
        canvas.drawTextBlob(builder.make(), offsetx, offsety + GetLineHeight(), paint);

        if (ellipsisTypography_) {
            ellipsisTypography_->Paint(canvas, offsetx + ellipsisOffsetX_, offsety + ellipsisOffsetY_);
        }

        if (underline) {
            struct LineMetrics {
                int32_t left;
                double right;
            };
            std::map<int32_t, struct LineMetrics> lineMetrics;
            for (const auto &[_, x, y, width] : glyphs_) {
                auto it = lineMetrics.find(y);
                if (it == lineMetrics.end()) {
                    lineMetrics[y] = {x, x + width};
                    continue;
                }

                if (lineMetrics[y].left > x) {
                    lineMetrics[y].left = x;
                }

                if (lineMetrics[y].right < x + width) {
                    lineMetrics[y].right = x + width;
                }
            }

            for (const auto &[y, metrics] : lineMetrics) {
                const auto &linepos = offsety + y + GetLineHeight() + GetUnderlinePos();
                canvas.drawLine(offsetx + metrics.left, linepos,
                                offsetx + metrics.right, linepos, paint);
            }
        }
    }

private:
    struct GlyphInfo {
        uint32_t glyphid;
        int32_t posX;
        int32_t posY;
        double width;
    };

    float lineWidth_ = 0;
    std::vector<GlyphInfo> glyphs_;
    std::unique_ptr<Typography> ellipsisTypography_ = nullptr;
    float ellipsisOffsetX_ = 0;
    float ellipsisOffsetY_ = 0;
};

class MySkiaFramework : public SkiaFramework {
    Typography tp[2];

public:
    MySkiaFramework()
    {
        std::string str1 = "12345 67890! مرحبا العالم";
        tp[0].text_ = str1;
        tp[0].underline = true;
        tp[0].Layout(300);

        std::string str2 = "مرحبا العالم 12345 67890!";
        tp[1].text_ = str2;
        tp[1].underline = true;
        tp[1].Layout(300);
    }

    void DrawAfter(SkCanvas &canvas) override
    {
        SkiaFramework::DrawAfter(canvas);
        tp[0].Paint(canvas, 100, 100);
        tp[1].Paint(canvas, 500, 100);
    }
};

int main(int argc, char **argv)
{
    MySkiaFramework().Run();
    return 0;
}
